/**
 *Project: jh-framework
 *File: cn.xkshow.framework.dao.hibernate.UniversalDao.java 
 *Author: hardy(admin@xkshow.cn) Copyright
 ****************************************************************
 *   VERSION       DATE            BY       CHANGE/COMMENT
 * --------------------------------------------------------------
 *    1.0        2013-7-4         hardy         create
 ****************************************************************
 *note:
 ***************************************************************/
package cn.xkshow.framework.dao.hibernate;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import cn.xkshow.lang.StringHelper;
import cn.xkshow.plugins.page.PageList;
import cn.xkshow.plugins.page.Page;

@Repository
public class UniversalDao<T> extends HibernateDaoSupport {

	/**
	 *************************************** 
	 * save-update
	 *************************************** 
	 */
	public void save(Object entity) {
		// TODO Auto-generated method stub
		this.getHibernateTemplate().save(entity);
	}

	public void saveOrUpdate(Object entity) {
		// TODO Auto-generated method stub
		this.getHibernateTemplate().saveOrUpdate(entity);
	}

	/**
	 *************************************** 
	 * delete
	 *************************************** 
	 */
	public void delete(Object entity) {
		// TODO Auto-generated method stub
		this.getHibernateTemplate().delete(entity);
	}

	public void deleteById(Class entity, Serializable id) {
		// TODO Auto-generated method stub
		this.getHibernateTemplate().delete(this.getEntity(entity, id));
	}

	public void deleteAll(String entityName, String propertyName,
			Object propertyValue) {
		// TODO Auto-generated method stub
		this.getHibernateTemplate().deleteAll(
				this.findList(true, entityName, 
						propertyName, "in", propertyValue));
	}

	public void deleteAllByHql(String hql, Map<String, Object> paramMap) {
		// TODO Auto-generated method stub
		this.getHibernateTemplate().deleteAll(
				this.findList(true, hql, paramMap));
	}

	/**
	 *************************************** 
	 * getEntity
	 *************************************** 
	 */
	public T getEntity(Class<T> entity, Serializable value) {
		// TODO Auto-generated method stub
		return this.getHibernateTemplate().get(entity, value);
	}

	public T getEntity(Class<T> entity, String propertyName,
			Object propertyValue) {
		// TODO Auto-generated method stub
		return (T) this.getEntityByQuery(true, null, entity.getName(), propertyName,
				propertyValue);
	}

	public Object getEntityByHql(String hql, Map<String, Object> paramMap) {
		// TODO Auto-generated method stub
		return this.getEntityByQuery(true, hql, paramMap);
	}

	public Object getEntityBySql(String sql, Map<String, Object> paramMap) {
		// TODO Auto-generated method stub
		return this.getEntityByQuery(false, sql, paramMap);
	}

	/**
	 *************************************** 
	 * getCount
	 *************************************** 
	 */
	public long getCountByHql(String hql, Map<String, Object> paramMap) {
		return getCountByQuery(true, hql, paramMap);
	}

	public long getCountBySql(String sql, Map<String, Object> paramMap) {
		return getCountByQuery(false, sql, paramMap);
	}

	/**
	 *************************************** 
	 * findList: HQL
	 *************************************** 
	 */
	public List findListByHql(String hql, Map<String, Object> paramMap) {
		return findList(true, hql, paramMap);
	}

	public List findListByHql(String hql, Map<String, Object> paramMap,
			int offset, int count) {
		return findList(true, hql, paramMap, offset, count);
	}

	public PageList findPageListByHql(String hql, Map<String, Object> paramMap,
			Page pages) {
		return this.findPageList(true, hql, paramMap, pages);
	}

	/**
	 *************************************** 
	 * findList: SQL
	 *************************************** 
	 */
	public List findListBySql(String sql, Map<String, Object> paramMap) {
		return this.findList(false, sql, paramMap);
	}

	public List findListBySql(String sql, Map<String, Object> paramMap,
			int offset, int count) {
		return this.findList(false, sql, paramMap, offset, count);
	}

	public PageList findPageListBySql(String sql, Map<String, Object> paramMap,
			Page pages) {
		return this.findPageList(false, sql, paramMap, pages);
	}

	/**
	 *************************************** 
	 * private *
	 *************************************** 
	 */
	/** getEntity */
	private Object getEntityByQuery(boolean isHql, String whereModel,
			String entityName, String propertyName, Object propertyValue) {
		// TODO Auto-generate dmethod stub
		List lst = this.findList(isHql, entityName, propertyName, whereModel,
				propertyValue);

		if (lst == null || lst.isEmpty()) {
			return null;
		} else {
			return (Object) lst.get(0);
		}
	}

	private Object getEntityByQuery(boolean isHql, String queryString,
			Map<String, Object> paramMap) {
		// TODO Auto-generate dmethod stub
		List lst = this.findList(isHql, queryString, paramMap);

		if (lst == null || lst.isEmpty()) {
			return null;
		} else {
			return (Object) lst.get(0);
		}
	}

	/** getCountByQuery */
	private String getCountByQuery(String query) {
		return "SELECT COUNT(*) " + query.substring(query.toUpperCase().indexOf("FROM"));
	}

	private long getCountByQuery(boolean isHql, String queryString,
			Map<String, Object> paramMap) {
		List<Long> lst = this.findList(isHql, getCountByQuery(queryString),
				paramMap);
		if (null != lst && !lst.isEmpty()) {
			return lst.get(0);
		} else {
			return 0;
		}
	}

	/** findList */
	private List findList(final boolean isHql, final String queryString,
			final Map<String, Object> paramMap) {
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session s) throws HibernateException,
					SQLException {
				Query query = null;
				if (isHql) {
					query = s.createQuery(queryString);
				} else {
					query = s.createSQLQuery(queryString);
				}
				if (null != paramMap && !paramMap.isEmpty()) {
					query.setProperties(paramMap);
				}

				return query.list();
			}
		});
	}

	/*@param: whereModel: =、in、like..*/
	private List findList(boolean isHql, String entityName,
			String propertyName, String whereModel, Object propertyValue) {
		// TODO Auto-generated method stub
		StringBuffer queryString = new StringBuffer("from " + entityName
				+ " as model");
		queryString.append(" where model." + propertyName);
		if (StringHelper.isNotNull(whereModel)) {
			if (whereModel.equalsIgnoreCase("in")) {
				queryString.append(" in (:propertyValue)");
			} else {
				queryString.append(" " + whereModel);
				queryString.append(" :propertyValue");
			}
		} else {
			queryString.append(" = :propertyValue");
		}

		Map<String, Object> paramMap = new HashMap<String, Object>();
		paramMap.put("propertyValue", propertyValue);

		return this.findList(isHql, queryString.toString(), paramMap);
	}
	
	private List findList(final boolean isHql, final String queryString,
			final Map<String, Object> paramMap, final int offset,
			final int count) {
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session s) throws HibernateException,
					SQLException {
				Query query = null;
				if (isHql) {
					query = s.createQuery(queryString);
				} else {
					query = s.createSQLQuery(queryString);
				}

				if (null != paramMap && !paramMap.isEmpty()) {
					query.setProperties(paramMap);
				}

				query.setFirstResult(offset);
				query.setMaxResults(count);
				return query.list();
			}
		});
	}
	
	private PageList findPageList(boolean isHql, String queryString, Map<String, Object> paramMap,
			Page pages) {
		if (pages.getDtotal() < 1) {
			pages.setDtotal(this.getCountByQuery(isHql, queryString, paramMap));
		}
		pages.executeCalculate();

		List list = null;
		if (pages.getDtotal() > 0) {
			list = this.findList(isHql, queryString, paramMap, pages.getDindex(), pages
					.getPerPageNum());
		}

		PageList pl = new PageList();
		pl.setObjectList(list);
		pl.setPage(pages);

		return pl;
	}

}
